package jmine.tec.persist.schema.creation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import jmine.tec.persist.schema.api.ProcessingSchemaElement;
import jmine.tec.persist.schema.api.SqlDialect;
import jmine.tec.persist.schema.impl.dialects.HibernateOracle10gDialect;
import jmine.tec.persist.schema.impl.parsers.SchemaParser;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.EntityMode;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata;

import bancosys.tec.persist.hibernate.PartitionSessionFactory;

/**
 * Statement creator que cria todo o schema necessário para o mapeamento das classes dadas. O schema é gerado no dialeto oracle e é esperado
 * que seja usado uma instancia apropriada de DbExecutor para a execução dos statements.
 * 
 * @author lundberg
 */
public class FullSchemaCreator {

    public static final Log LOG = LogFactory.getLog(FullSchemaCreator.class);

    private SqlDialect dialect;

    /**
     * Construtor
     * 
     * @param dialect dialect
     */
    public FullSchemaCreator(SqlDialect dialect) {
        this.dialect = dialect;
    }

    /**
     * {@inheritDoc}
     */
    public final String[] createStatements(SessionFactory sessionFactory) {
        List<Class<?>> mappedClasses = new ArrayList<Class<?>>();
        for (Iterator<?> it = sessionFactory.getAllClassMetadata().values().iterator(); it.hasNext();) {
            ClassMetadata meta = (ClassMetadata) it.next();
            mappedClasses.add(meta.getMappedClass(EntityMode.POJO));
        }
        if (PartitionSessionFactory.class.isAssignableFrom(sessionFactory.getClass())) {
            PartitionSessionFactory sf = (PartitionSessionFactory) sessionFactory;
            return this.doCreateStatements(sf.getConfiguration());
        } else {
            return this.createStatements(mappedClasses);
        }
    }

    /**
     * {@inheritDoc}
     */
    public final String[] createStatements(Configuration configuration) {
        List<Class<?>> mappedClasses = new ArrayList<Class<?>>();
        for (Iterator<?> it = configuration.getClassMappings(); it.hasNext();) {
            PersistentClass meta = (PersistentClass) it.next();
            mappedClasses.add(meta.getMappedClass());
        }
        return this.doCreateStatements(configuration);
    }

    /**
     * {@inheritDoc}
     */
    public final String[] createStatements(List<Class<?>> mappedClasses) {
        return this.doCreateStatements(mappedClasses);
    }

    /**
     * Cria os statements a partir da lista de classes.
     * 
     * @param mappedClasses mappedClasses
     * @return Array de statements
     */
    private String[] doCreateStatements(List<Class<?>> mappedClasses) {
        return this.doCreateStatements(this.createConfiguration(mappedClasses));
    }

    /**
     * Cria uma configuração com a lista de classes dada.
     * 
     * @param mappedClasses mappedClasses
     * @return Configuration
     */
    private Configuration createConfiguration(List<Class<?>> mappedClasses) {
        AnnotationConfiguration configuration = new AnnotationConfiguration();
        for (Class<?> mapped : mappedClasses) {
            configuration.addAnnotatedClass(mapped);
        }
        return configuration;
    }

    /**
     * Cria os statements a partir das subclasses. A operação padrão
     * 
     * @param configuration configuration
     * @return Array de statements
     */
    private String[] doCreateStatements(Configuration configuration) {
        SchemaParser parser = new SchemaParser();
        ProcessingSchemaElement<Configuration> schema =
                parser.parse(Arrays.asList(configuration.generateSchemaCreationScript(new HibernateOracle10gDialect())));
        schema.process(configuration);
        return schema.createStatements(this.dialect).toArray(new String[0]);
    }
}
